<template>
  <div class="editor-out-container">
    <Toolbar
      :editor="editor"
      :default-config="toolbarConfig"
      :mode="mode"
      :style="{ width: `${editorWidth}px` }"
      style="border-bottom: 1px solid #ccc;"
    />
    <Editor
      v-loading="uploading"
      v-model="computedValue"
      :default-config="editorConfig"
      :mode="mode"
      :style="{ height: `${editorHeight}px`, width: `${editorWidth}px` }"
      class="editor-container"
      @onCreated="onCreated"
      @onChange="onChange"
    />
    <mention-modal
      v-if="isShowMention && isShowModal"
      :editor="editor"
      :toolbar-top="toolbarTop"
      :padding-left="paddingLeft"
      @hideMentionModal="hideMentionModal"
      @closeMentionModal="closeMentionModal"
      @insertMention="insertMention"
    />
  </div>
</template>

<script>
import '@wangeditor/editor/dist/css/style.css';
import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
import MentionModal from './MentionModal';
import { Boot } from '@wangeditor/editor';
import mentionModule from '@wangeditor/plugin-mention';
import { MentionButton } from './customMention';
const { BASE_API } = process.env;
import { mapState } from 'vuex';
import { aesDecode } from '@/utils/string';
import { SECRET_KEY } from '@/utils/const';
import axios from 'axios';
import store from '@/store';
import { getToken } from '@/utils/auth';

export default {
  name: 'RichEditor',
  components: { Editor, Toolbar, MentionModal },
  model: {
    prop: 'value',
    event: 'changeValue'
  },
  props: {
    value: {
      type: String,
      default: ''
    },
    mode: {
      type: String,
      default: 'simple' // 可选项 'default', 是否简洁模式
    },
    isShowMention: { // 是否有@功能
      type: Boolean,
      default: false
    },
    defaultMention: {
      type: Object,
      default: () => null
    },
    editorHeight: {
      type: Number,
      default: 320
    },
    editorWidth: { // 编辑器宽度
      type: Number,
      default: 550
    },
    imgRatio: { // 图片缩放比例
      type: Number,
      default: 30
    },
    videoWidth: { // 视频宽度
      type: Number,
      default: 542
    },
    videoHeight: { // 视频高度
      type: Number,
      default: 304
    },
    paddingLeft: { // 弹窗左边距，定位@浮窗需要用到
      type: Number,
      default: 25
    },
    importApi: {
      type: String,
      default: ''
    },
    element: {
      type: Object,
      default: null
    }
  },
  data() {
    return {
      editor: null,
      isShowModal: false,
      toolbarTop: 0,
      uploading: false
    };
  },
  computed: {
    ...mapState({
      'isRegisterMenu': state => state.richEditor.isRegisterMenu
    }),
    isWeiboOrWeixin() {
      return this.importApi === '/common/file/upload_v2';
    },
    computedValue: {
      get() {
        return this.value;
      },
      set(val) {
        const regex = /(<img[^>]*?)\sstyle="\s*"(.*?>)/g;
        const initImagesWidth = val.replace(regex, `$1 style="width: ${this.imgRatio}%" $2`);
        const initVideosWidth = initImagesWidth.replace(/width="auto" height="auto"/g, `width=${this.videoWidth} height=${this.videoHeight}`);
        this.$nextTick(() => {
          this.$emit('changeValue', initVideosWidth);
        });
      }
    },
    toolbarConfig() {
      const baseConfig = [
        {
          'key': 'group-more-style',
          'title': '更多',
          'iconSvg': '<svg viewBox="0 0 1024 1024"><path d="M939.939489 459.072557 562.339502 459.072557 562.339502 83.519182 462.055494 83.519182 462.055494 459.072557 84.455507 459.072557 84.455507 559.356564 462.055494 559.356564 462.055494 939.003164 562.339502 939.003164 562.339502 559.356564 939.939489 559.356564Z"></path></svg>',
          'menuKeys': [
            'headerSelect', 'fontSize', 'fontFamily', 'lineHeight',
            'insertImage', 'deleteImage', 'editImage', 'editVideoSize',
            'imageWidth30', 'imageWidth50', 'imageWidth100', 'divider',
            'insertLink', 'editLink', 'unLink', 'viewLink', 'blockquote',
            'todo', 'enter', 'deleteTable', 'insertTableRow', 'deleteTableRow',
            'insertTableCol', 'deleteTableCol', 'tableHeader'
          ]
        },
        // 'undo',
        // 'redo',
        'bold',
        'color',
        'bgColor',
        {
          'key': 'group-more-style',
          'title': '更多',
          'iconSvg': '<svg viewBox="0 0 1024 1024"><path d="M204.8 505.6m-76.8 0a76.8 76.8 0 1 0 153.6 0 76.8 76.8 0 1 0-153.6 0Z"></path><path d="M505.6 505.6m-76.8 0a76.8 76.8 0 1 0 153.6 0 76.8 76.8 0 1 0-153.6 0Z"></path><path d="M806.4 505.6m-76.8 0a76.8 76.8 0 1 0 153.6 0 76.8 76.8 0 1 0-153.6 0Z"></path></svg>',
          'menuKeys': [
            'through',
            'underline',
            'italic',
            'sup',
            'sub',
            'clearStyle'
          ]
        },
        'bulletedList',
        'numberedList',
        {
          'key': 'group-justify',
          'title': '对齐',
          'iconSvg': '<svg viewBox="0 0 1024 1024"><path d="M768 793.6v102.4H51.2v-102.4h716.8z m204.8-230.4v102.4H51.2v-102.4h921.6z m-204.8-230.4v102.4H51.2v-102.4h716.8zM972.8 102.4v102.4H51.2V102.4h921.6z"></path></svg>',
          'menuKeys': [
            'justifyLeft',
            'justifyRight',
            'justifyCenter',
            'justifyJustify',
            'indent',
            'delIndent'
          ]
        },
        'insertTable',
        'emotion',
        'uploadImage',
        'uploadVideo'
      ];
      return {
        toolbarKeys: this.isShowMention ? [...baseConfig, 'mentionUser'] : baseConfig
      };
    },
    editorConfig() {
      return {
        placeholder: this.element && this.element.options && this.element.options.placeholder ? this.element.options.placeholder : '请输入...',
        EXTEND_CONF: {
          mentionConfig: {
            showModal: this.showMentionModal,
            hideModal: this.hideMentionModal
          }
        },
        MENU_CONF: {
          uploadImage: {
            customUpload: this.customUploadImg
          },
          uploadVideo: {
            customUpload: this.customUploadVideo
          }
        }
      };
    },
    mentionConf() {
      const showMentionModal = this.showMentionModal;
      return {
        key: 'mentionUser', // 定义 menu key
        factory() {
          return new MentionButton(showMentionModal);
        }
      };
    }
  },
  created() {
    Boot.registerModule(mentionModule);
    // 注册自定义菜单
    if (this.isShowMention && !this.isRegisterMenu) {
      Boot.registerMenu(this.mentionConf);
      this.$store.dispatch('changeRegisterMenu', true);
    }
    window.addEventListener('message', (e) => {
      if (e.data.type === 'RichEditor-setDangerouslyInsertHtml') {
        this.setDangerouslyInsertHtml(e.data.data);
      }
    });
  },
  beforeDestroy() {
    const editor = this.editor;
    if (editor == null) return;
    editor.destroy(); // 销毁编辑器
  },
  methods: {
    setDangerouslyInsertHtml(html) {
      setTimeout(() => {
        this.editor.dangerouslyInsertHtml(html);
      });
    },
    onCreated(editor) {
      // 一定要用 Object.seal() ，否则会报错
      this.editor = Object.seal(editor);
      if (this.defaultMention) {
        const { id, userName } = this.defaultMention;
        this.insertMention(id, userName);
        const timer = setTimeout(() => {
          // 恢复选区
          this.editor.restoreSelection();
          // 光标要移动两次，否则无法变成在默认@人员后聚焦待输入的状态
          this.editor.move(1);
          this.editor.move(1);
          clearTimeout(timer);
        });
      }
      this.editor.blur();
      // 获取工具栏底部距离弹窗顶部的高度
      if (this.isShowMention) {
        this.$nextTick(() => {
          const toolbarDom = document.querySelector('.w-e-toolbar');
          if (toolbarDom) {
            const timer = setTimeout(() => {
              const domHeight = toolbarDom.offsetHeight;
              const domTop = toolbarDom.offsetTop;
              this.toolbarTop = domHeight + domTop;
              clearTimeout(timer);
            });
          }
        });
      }
    },
    onChange(editor) {
      this.$emit('changeValue', editor.getHtml());
      this.$emit('change', editor.getHtml());
    },
    showMentionModal() {
      this.isShowModal = true;
    },
    hideMentionModal() {
      this.isShowModal = false;
    },
    closeMentionModal() {
      this.hideMentionModal();
      this.editor.restoreSelection();
    },
    customUploadImg(file, insertFn) {
      this.uploading = true;
      const api = this.importApi || '/common/ueditor/upload?action=uploadimage';
      this.uploadFileMethod({ file }, api).then(res => {
        res.url && insertFn(res.url, '');
        this.uploading = false;
      }).catch(() => {
        this.uploading = false;
      });
    },
    customUploadVideo(file, insertFn) {
      this.uploading = true;
      const api = this.importApi || '/common/ueditor/upload?action=uploadvideo';
      this.uploadFileMethod({ file }, api).then(res => {
        res.url && insertFn(res.url, '');
        this.uploading = false;
      }).catch(() => {
        this.uploading = false;
      });
    },
    // 插入@人员
    insertMention(id, name) {
      const mentionNode = {
        type: 'mention', // 必须是 'mention'
        value: name,
        info: { id },
        children: [{ text: '' }] // 必须有一个空 text 作为 children
      };
      if (this.editor) {
        this.editor.restoreSelection();
        this.editor.deleteBackward('character'); // 删除 '@'
        this.editor.insertNode(mentionNode); // 插入 mention
        this.$nextTick(() => {
          this.editor.restoreSelection(); // 恢复选区
          this.editor.move(1);
        });
      }
    },
    uploadFileMethod(param, url) {
      return new Promise((resolve) => {
        const fileObject = param.file;
        const formData = new FormData();
        if (this.isWeiboOrWeixin) {
          const fileExtension = fileObject.name.split('.').pop();
          const fileName = `${new Date().valueOf()}_${Math.ceil(Math.random() * 99999)}.${fileExtension}`;
          formData.append('fileData', fileObject);
          formData.append('fileName', fileName);
        } else {
          formData.append('upfile', fileObject);
        }
        this.toUploadHttp(formData, url, (data) => {
          if (this.isWeiboOrWeixin && data.data) {
            resolve(data.data);
          } else {
            resolve(data);
          }
        });
      });
    },
    toUploadHttp(formData, url, callback) {
      const service = axios.create({
        baseURL: BASE_API, // api 的 base_url
        timeout: 3600000, // 请求超时时间
        // timeout: 60000,
        headers: {
          'Content-Type': 'multipart/form-data;'
        }
      });
      this.serviceToken(service);
      this.importList(service, url, formData, callback);
    },
    importList(service, url, formData, callback) {
      service.post(url, formData).then((res) => {
        if (res.data) {
          callback(res.data);
        } else {
          this.$message.error('上传失败');
        }
      }).catch((err) => {
        let errMsg = typeof err === 'object' ? err.message : err;
        if (errMsg.indexOf('timeout') >= 0) {
          errMsg = '文件较大，拼命导入中，请半小时后进入系统查看导入结果';
        }
        if (errMsg === 'Network Error') {
          this.$message.warning('网络错误！请检查网络状态...');
        }
        this.$emit('onError', errMsg || '请求失败');
      });
    },
    serviceToken(service) {
      service.interceptors.request.use(
        config => {
          const token = store.getters.token || getToken();
          if (token) {
            config.headers.common['token'] = token;
          }
          return config;
        },
        error => {
          console.log(error);
          Promise.reject(error);
        }
      );
      service.interceptors.response.use(response => {
        if (response && response.data && typeof response.data === 'string') {
          const decodeStr = aesDecode(response.data, SECRET_KEY);
          try {
            response.data = typeof decodeStr === 'string' ? JSON.parse(decodeStr) : decodeStr;
          } catch (error) {
            response.data = decodeStr;
          }
        }
        return response;
      });
    },
    // 插入文本
    insertText(val) {
      this.editor.restoreSelection();
      this.editor.insertText(val);
    },
    // 重置内容
    setHtml(html) {
      this.editor.setHtml(html);
    }
  }
};
</script>

<style scoped lang="scss">
.editor-out-container {
  border: 1px solid #ccc;
  border-radius: 5px;
  overflow: hidden;
  .editor-container {
    //overflow-y: hidden;
    &::v-deep {
      .w-e-scroll {
        > div:first-child{
          height: calc(100% - 1px);
        }
      }
      .w-e-modal {
        .babel-container {
          display: flex;
          width: calc(100% - 20px);
          > span {
            white-space: nowrap;
            margin-right: 10px;
          }
        }
        .button-container {
          text-align: center;
          > button {
            width: 80px;
          }
        }
      }
    }
  }
}

</style>
